#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <stdio.h>
#include <sys/stat.h>

#include "awb/rrr/service_ids.h"

#include "awb/provides/connected_application.h"

using namespace std;

unsigned int reverse_uint(unsigned int o) {
	union {
		unsigned int i;
		unsigned char c[4];
	} ita;
	ita.i = o;
	unsigned char t = ita.c[0];
	ita.c[0] = ita.c[3];
	ita.c[3] = t;
	t = ita.c[1];
	ita.c[1] = ita.c[2];
	ita.c[2] = t;
	return ita.i;
}

unsigned int total_loads = 0;
void decompress_done() {
	printf( "Decompression Done!\n" );
	printf( "Total Instr Cache misses: %d\n", total_loads );
	fflush(stdout);
	exit(0);
}
//FIXME:: the varbit and REG stuff isn't done in the binary output. FIXME!!
unsigned long long output_decompressed_count;
unsigned int *decompr_buff;
unsigned int decompr_buff_idx;
void output_decompressed_bin(FILE* fdecompressed, unsigned int flags, unsigned int pc, unsigned int addr, unsigned int value) {
	if ( flags >> 31 ) {
		fwrite(decompr_buff, sizeof(unsigned int), decompr_buff_idx, fdecompressed);
		printf( "end!!%llu\nforcedwrite - %d\n", output_decompressed_count, decompr_buff_idx );
		fflush(stdout);
		decompress_done();
		return;
	}

	decompr_buff[decompr_buff_idx] = flags;
	decompr_buff[decompr_buff_idx+1] = addr;
	decompr_buff[decompr_buff_idx+2] = value;
	decompr_buff[decompr_buff_idx+3] = pc;
	decompr_buff_idx += 4;
/*
	unsigned int data[4];
	data[0] = flags;
	data[1] = addr;
	data[2] = value;
	data[3] = pc;
	*/
	if ( decompr_buff_idx >= COMPR_BUFF_SIZE - 4 ) {
		fwrite(decompr_buff, sizeof(unsigned int), decompr_buff_idx, fdecompressed);
//		printf( "write - %d\n", decompr_buff_idx );
//		fflush(stdout);
		decompr_buff_idx = 0;//(decompr_buff_idx + 4)%COMPR_BUFF_SIZE;
	}
}

void output_decompressed_string(FILE* fdecomprstring, unsigned int flags, unsigned int pc, unsigned int addr, unsigned int value) {
	unsigned char regChange = (flags>>16)&1;
	unsigned char memAcc = (flags>>17)&3;
	unsigned char size = (flags>>19)&31;
	unsigned char target = (flags>>24)&31;

	if (memAcc == 1) { // memAccRead
		fprintf( fdecomprstring, "MEM_READ %012x %02d %x\n", addr, size, value );
	} else if (memAcc == 2) {
		fprintf( fdecomprstring, "MEM_WRITE %012x %02d %x\n", addr, size, value );
	}
	if (regChange) {
		fprintf( fdecomprstring, "REG %02d 00 %x\n", target, value );
	}
	if ( pc ) fprintf( fdecomprstring, "STEP%x\n#\n", pc );

}

void output_decompressed_dinero(FILE* f, unsigned int flags, unsigned int pc, unsigned int addr) {
	unsigned char memAcc = (flags>>17)&3;
	if ( flags >> 31 ) {
		printf( "end!!%llu\nforcedwrite - %d\n", output_decompressed_count, decompr_buff_idx );
		//fflush(stdout);
		decompress_done();
	}
	fprintf(f, "2 %x\n", pc);

	if (memAcc == 1) { // memAccRead
		fprintf(f, "0 %x\n", addr);
	} else if (memAcc == 2) {
		fprintf(f, "1 %x\n", addr);
	}
}
// ===== service instantiation =====
PROCESSORSYSTEMRRR_SERVER_CLASS PROCESSORSYSTEMRRR_SERVER_CLASS::instance;

// constructor
PROCESSORSYSTEMRRR_SERVER_CLASS::PROCESSORSYSTEMRRR_SERVER_CLASS() //:
  //clientStub(new PROCESSORSYSTEMRRR_CLIENT_STUB_CLASS(this)),
//  serverStub(new PROCESSORSYSTEMRRR_SERVER_STUB_CLASS(this))
{
		// wjun
		FILE* fexec = fopen("exec.bin", "rb");
		if ( fexec ) {
			int t;
			t = fread(&pc, sizeof(unsigned int), 1, fexec);
			t = fread(&ioffset, sizeof(unsigned int), 1, fexec);
			t = fread(&isize, sizeof(unsigned int), 1, fexec);
			idata = (unsigned int*)malloc(isize + 32);
			t = fread(idata, isize, 1, fexec);
			
			fclose(fexec);
			printf( "exec file opened:%x[%x]\n", ioffset ,isize );
		} else {
			printf( "exec.bin not found!!\n" );
			pc = 0;
			ioffset = 0;
			isize = 0;
			idata = NULL;
			exit(1);
		}
//		fcompressed = fopen("compressed.bin", "rb");
//		fdecomprstring = fopen("decompr.txt", "w");
//		fdecomprbin = fopen("decompr.bin", "w");

		fdinerofifo = fopen("dinerofifo", "w");
		if ( /*!fcompressed || !fdecomprstring || !fdecomprbin ||*/ !fdinerofifo ) {
			printf( "Some file failed to open!!\n" );
			fflush(stdout);
			exit(1);
		}
		compr_buff = (unsigned int*)malloc(sizeof(unsigned int)*COMPR_BUFF_SIZE);
//		compr_buff_loaded = fread(compr_buff, sizeof(unsigned int), COMPR_BUFF_SIZE/2, fcompressed);
		compr_buff_idx = 0;
		compr_buff_lowhalf_loaded = 1;
		compr_buff_highhalf_loaded= 0;
		decompr_buff = (unsigned int*)malloc(sizeof(unsigned int)*COMPR_BUFF_SIZE);
		decompr_buff_idx = 0;

    // instantiate stub
    //memory = NULL;
    fflush(stdout);
}


// destructor
PROCESSORSYSTEMRRR_SERVER_CLASS::~PROCESSORSYSTEMRRR_SERVER_CLASS()
{
    Cleanup();
}

// init
void
PROCESSORSYSTEMRRR_SERVER_CLASS::Init(PLATFORMS_MODULE p)
{
    parent = p;
}

// uninit
void
PROCESSORSYSTEMRRR_SERVER_CLASS::Uninit()
{
    Cleanup();
}

// cleanup
void
PROCESSORSYSTEMRRR_SERVER_CLASS::Cleanup()
{
    delete serverStub;
}


//
// RRR service methods
//
// wjun
//UINT32
OUT_TYPE_InstrRequestLoad
PROCESSORSYSTEMRRR_SERVER_CLASS::InstrRequestLoad (UINT32 address)
{
//  UINT32 returnVal;
	OUT_TYPE_InstrRequestLoad rv;
	rv.value0 = 0;
	rv.value1 = 0;
	rv.value2 = 0;
	rv.value3 = 0;
	rv.value4 = 0;
	rv.value5 = 0;
	rv.value6 = 0;
	rv.value7 = 0;
	unsigned int idx = (address - ioffset);
	total_loads ++;

	
  //returnVal = 0xcccccc;
	if ( idx >= 0 && idx < isize ) {
		rv.value0 = reverse_uint( idata[idx>>2] );
		rv.value1 = reverse_uint( idata[(idx>>2)+1] );
		rv.value2 = reverse_uint( idata[(idx>>2)+2] );
		rv.value3 = reverse_uint( idata[(idx>>2)+3] );
		rv.value4 = reverse_uint( idata[(idx>>2)+4] );
		rv.value5 = reverse_uint( idata[(idx>>2)+5] );
		rv.value6 = reverse_uint( idata[(idx>>2)+6] );
		rv.value7 = reverse_uint( idata[(idx>>2)+7] );
		printf( "Fetched %x -> %x\n", address, rv.value0 );
	}
	printf( "Yay?!%x\n", address ); fflush(stdout);
	return rv;
}

void PROCESSORSYSTEMRRR_SERVER_CLASS::SendResult(UINT32 flags1, UINT32 addr1, UINT32 value1, UINT32 pc1, 
				UINT32 flags2, UINT32 addr2, UINT32 value2, UINT32 pc2, 
				UINT32 flags3, UINT32 addr3, UINT32 value3, UINT32 pc3,
				UINT32 flags4, UINT32 addr4, UINT32 value4, UINT32 pc4
				) 
{
	output_decompressed_string(fdecomprstring, flags1, pc1, addr1, value1);
	output_decompressed_string(fdecomprstring, flags2, pc2, addr2, value2);
	output_decompressed_string(fdecomprstring, flags3, pc3, addr3, value3);
	output_decompressed_string(fdecomprstring, flags4, pc4, addr4, value4);

	/*
	output_decompressed_bin(fdecomprbin, flags1, pc1, addr1, value1);
	output_decompressed_bin(fdecomprbin, flags2, pc2, addr2, value2);
	output_decompressed_bin(fdecomprbin, flags3, pc3, addr3, value3);
	output_decompressed_bin(fdecomprbin, flags4, pc4, addr4, value4);
*/
	output_decompressed_dinero(fdinerofifo, flags1, pc1, addr1);
	output_decompressed_count += 1;
	output_decompressed_dinero(fdinerofifo, flags2, pc2, addr2);
	output_decompressed_count += 1;
	output_decompressed_dinero(fdinerofifo, flags3, pc3, addr3);
	output_decompressed_count += 1;
	output_decompressed_dinero(fdinerofifo, flags4, pc4, addr4);
	output_decompressed_count += 1;
//	printf( "decompr pc1: %x\n", pc1 );
}

bool
PROCESSORSYSTEMRRR_SERVER_CLASS::SingleCompressed(unsigned int* rv)
{
/*
	rv[1] = 0;
	rv[2] = 0;
	rv[3] = 0;

	if ( compr_buff_idx >= COMPR_BUFF_SIZE ) {
		compr_buff_idx = compr_buff_idx % COMPR_BUFF_SIZE;
	}
	
	if ( compr_buff_idx >= compr_buff_loaded && feof(fcompressed )) {
//		printf( "end! %x %x\n", compr_buff_idx, compr_buff_loaded );
//		fflush(stdout);
		rv[0] = 1 << 31;
		return true;
	}
	
	if (!compr_buff_lowhalf_loaded && compr_buff_idx > COMPR_BUFF_SIZE/2) {
		compr_buff_highhalf_loaded = 0;
		compr_buff_lowhalf_loaded = 1;
		if ( fcompressed && !feof(fcompressed) ) {
			compr_buff_loaded = fread(compr_buff, sizeof(unsigned int), COMPR_BUFF_SIZE/2, fcompressed);
		}
	} else if (!compr_buff_highhalf_loaded && compr_buff_idx < COMPR_BUFF_SIZE/2) {
		if ( fcompressed && !feof(fcompressed) ) {
			compr_buff_loaded = fread(compr_buff + COMPR_BUFF_SIZE/2, sizeof(unsigned int), COMPR_BUFF_SIZE/2, fcompressed);
			compr_buff_loaded += COMPR_BUFF_SIZE/2;
		}
		compr_buff_highhalf_loaded = 1;
		compr_buff_lowhalf_loaded = 0;
	}

	rv[0] = compr_buff[compr_buff_idx++];
//	if (rv[0]%(1<<16)) {
//		compr_buff_idx += 1;
//		return false;
//	}

	if (rv[0]%(1<<30)) {
//		compr_buff_idx += 1;
		return false;
	}

	unsigned char regChange = (rv[0]>>16)&1;
	unsigned char memAcc = (rv[0]>>17)&3;

	// order of flags, addr, value, pc
	rv[1] = compr_buff[(compr_buff_idx++)%COMPR_BUFF_SIZE];
	if ( memAcc ) rv[2] = compr_buff[(compr_buff_idx++)%COMPR_BUFF_SIZE];
	if ( regChange ) rv[3] = compr_buff[(compr_buff_idx++)%COMPR_BUFF_SIZE];
//	compr_buff_idx += 4;
*/
	return false;
}

//static int g_comprReqCount;
//static pthread_mutex_t g_comprReqLock;
/*
void
PROCESSORSYSTEMRRR_SERVER_CLASS::ReqCompressed(UINT32 avail) {
	unsigned int sv[16] = {0,};
	int i;

	for ( i = 0; i < avail/4; i++ ) {
		SingleCompressed(sv);
		SingleCompressed(sv+4);
		SingleCompressed(sv+8);
		bool done = SingleCompressed(sv+12);

		clientStub->ResCompressed(
			sv[0], sv[1], sv[2], sv[3],		
			sv[4+0], sv[4+1], sv[4+2], sv[4+3],		
			sv[8+0], sv[8+1], sv[8+2], sv[8+3],		
			sv[12+0], sv[12+1], sv[12+2], sv[12+3]		
			);
//		if ( done ) break;
}
*/
